Skip to content

fix: normalize cross-provider tool call ids - #327

Merged
7Sageer merged 1 commit into
MoonshotAI:mainfrom
7Sageer:fix/tool-call-id-normalization
Jun 2, 2026
Merged

fix: normalize cross-provider tool call ids#327
7Sageer merged 1 commit into
MoonshotAI:mainfrom
7Sageer:fix/tool-call-id-normalization

Conversation

@7Sageer

@7Sageer 7Sageer commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue. This fixes provider switching failures observed when historical tool call IDs from one provider are invalid for another provider.

Problem

Some providers emit tool call IDs that are valid for their own protocol but rejected by another provider during session replay. For example, Kimi can emit IDs like Write:6, while Anthropic requires tool IDs to match ^[a-zA-Z0-9_-]+$, causing restored sessions to fail after switching providers.

What changed

Added a shared provider-boundary tool call ID normalizer for kosong and wired it into Anthropic, Kimi, OpenAI Chat Completions, and OpenAI Responses. The normalizer keeps valid IDs stable, rewrites invalid IDs only in outbound provider payloads, and preserves tool call/result matching with deterministic collision handling.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

Verification:

  • pnpm exec vitest run --config vitest.config.ts in packages/kosong
  • pnpm --filter @moonshot-ai/kosong run typecheck

@changeset-bot

changeset-bot Bot commented Jun 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 228628e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@moonshot-ai/kosong Patch
@moonshot-ai/kimi-code Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jun 2, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@228628e
npx https://pkg.pr.new/@moonshot-ai/kimi-code@228628e

commit: 228628e

@7Sageer
7Sageer merged commit 8809f3e into MoonshotAI:main Jun 2, 2026
9 checks passed
@github-actions github-actions Bot mentioned this pull request Jun 2, 2026
dv8128-agent Bot pushed a commit to dv8128/kimi-code that referenced this pull request Aug 1, 2026
Kimi models are trained to see tool-call ids echoed back in conversation
history in the canonical shape `functions.<name>:<idx>`. The
cross-provider id normalizer added in MoonshotAI#327 sanitizes these ids
(`functions.Read:0` -> `functions_Read_0`), after which Kimi emits
reasoning with no tool call and stops the turn (`APIEmptyResponseError`
/ `finishReason=stop`).

Kimi served through an OpenAI-compatible endpoint (litellm / vLLM /
SGLang / Azure) is configured as an `openai` provider, so the fix must
cover the OpenAI chat-completions and responses policies, not just the
native Kimi policy: that is the path in the linked issue, whose session
dump carries the canonical id `functions.Skill:0` in history. The
failure is masked on the official endpoint (which normalizes
server-side) but reproducible on self-hosted deployments, where the
history id format is the model's only cue.

Add `sanitizeToolCallIdPreservingNative` /
`sanitizeOpenAIResponsesCallIdPreservingNative`: a canonical native id
is passed through verbatim, everything else keeps the existing
normalization. Wire them into the OpenAI chat, OpenAI responses, and
Kimi tool-call-id policies in both the default (kosong) and
agent-core-v2 engines.

The carve-out is keyed on the id shape, so it applies to any `openai`
provider (Kimi behind such an endpoint does not self-identify as Kimi at
the policy layer, so the shape is the only signal). Trade-off: a
Kimi-authored id replayed onto a different, stricter OpenAI-compatible
backend is now passed through rather than sanitized; a canonical id is
nonetheless a valid free-form tool_call id under the OpenAI
chat-completions spec. Charset-restricted providers (Anthropic,
`^[a-zA-Z0-9_-]+$`) keep sanitizing, so MoonshotAI#327's Kimi -> Anthropic replay
fix is intact; Google (function names, not ids) is unaffected.

Resolve MoonshotAI#520.

Refs: Moonshot Kimi-K2 tool_call_guidance.md; vLLM blog "Chasing 100%
Accuracy: Debugging Kimi K2's Tool-Calling on vLLM".

Acked-by: Andrey Akinshin <andrey.akinshin@jetbrains.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant